#include <string>
#include <list>

using namespace std;
using std::string;
using std::list;

enum Distribution {
	DIST_TRIANGULAR,
	DIST_RANDOM,
	DIST_NORMAL,
	DIST_CONSTANT
};

enum BooleanOperator {
	BOP_EQL,
	BOP_NEQ,
	BOP_LT,
	BOP_GT,
	BOP_LE,
	BOP_GE
};

enum MathBinaryOperator {
	MBOP_PLUS,
	MBOP_MINUS,
	MBOP_TIMES,
	MBOP_DIVIDE,
	MBOP_EXP,
	MBOP_LOG
};


class Variable {
	public:
		Variable(string name);
		string name;

};

class Category{
	public:
		Category(string label, float probability);
		string label;
		float probability;		
};

class CategoricalVariable : Variable{
	public:
		CategoricalVariable(string name, list<Category> values);
		list<Category> values;
};

class NumericVariable : public Variable{
	public:
		NumericVariable(string name, float from, float to, Distribution distribution, float mean=0.0, float variance=0.0);
		float from;
		float to;
		Distribution distribution;
		float mean;
		float variance;
};



class Expression {
	public:
		Expression();
};

class BinaryExpression : Expression {
	public:
		BinaryExpression();
		MathBinaryOperator type;
		Expression operator1;
		Expression operator2;
};

class Number : Expression {
	public:
		float value;
};

class Label : Expression {
	public:
		float string;
};

class Id : Expression {
	public:
		Variable *variable;
		Group *group;
};

class Constraint {
	BooleanOperator type;
	Expression operator1;
	Expression operator2;
};

class Effect {
	Id operator1;
	Expression operator2;
};

class Interaction {
	public:
		Interaction();
		void addVariable(Variable instance);
		Expression probability;
		Constraint constraint;
		Effect effect;
		list<Group> groups;
};

class Group {
	public:
		Group(string name, int multiplicity);
		//void addVariable(Variable instance);
		string name;
		int multiplicity;
		
	
};

class Model {
	public:
		Model();
		void addGroup(string name, int multiplicity);
		void addCategoricalVariable(string name, string value, float probability);
		void addNumericVariable(string name, float from, float to, Distribution distribution, float mean=0.0, float variance=0.0);
		void printGroups(){
			list<Group>::iterator i;
			for(i=groups.begin(); i != groups.end(); ++i) 
				cout << i->name << " ";
			cout << endl;
		};
		string name;
		list<Group> groups;
		list<Variable> variables;
		
	
};

class ModelBuilder {
	public:
		virtual Model *getResult(){
        	return _model;
   		}
		virtual void buildGroup(string name, int multiplicity) = 0;
		virtual void buildCategoricalVariable(string name, string value, float probability) = 0;
		virtual void buildNumericalVariable(string name, float from, float to, Distribution distribution, float mean=0.0, float variance=0.0) = 0;
	protected:
		Model *_model;
};

class ConcreteModelBuilder : public ModelBuilder {
	public:
		ConcreteModelBuilder();
		void buildGroup(string name, int multiplicity);
		void buildCategoricalVariable(string name, string value, float probability);
		void buildNumericalVariable(string name, float from, float to, Distribution distribution, float mean=0.0, float variance=0.0);
};
